package controller;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;

import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractAction;

import gui.Gui;
import java.awt.Color;
import model.Aggregation;
import model.Association;
import model.Attribute;
import model.ClassObj;
import model.DomainModel;
import model.Inheritance;
import parser.*;
import dm.*;

import file.io.FileWriter;
import java.awt.SplashScreen;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import util.Singleton;

public class Controller {
	private DomainModel model;
	private Gui gui;
	private String selectedText;
	private DomainModelController dmController;
	private RemoveController removeController;
	private GenerateDomainModelController genDomainModelController;
	private List<ClassObj> classList;
	private List<Association> associationList;
	private List<Aggregation> aggregationList;
	private List<Inheritance> inheritanceList;
	private ClassObj classObj;
	private Association association;
	private Aggregation aggregation;
	private Inheritance inheritance;
        private DMParser parser;

        public Controller()
    {

    }

    public Controller(DomainModel model, Gui application)
    {
        this.model = model;
        this.gui = application;
        dmController = new DomainModelController(model);
        removeController = new RemoveController(model);
        genDomainModelController = new GenerateDomainModelController(model);


        initTestData();

        gui.addPopupMenuListener(new PopupMenuListener());
        gui.addAddClassAction(new AddClassAction());
        gui.addAddAttributeAction(new AddAttributeAction());
        gui.addAddAggregationAction(new AddAggregationAction());
        gui.addAddAssociationAction(new AddAssociationAction());
        gui.addAddInheritanceAction(new AddInheritanceAction());
        gui.addAddMultiplicityAction(new AddMultiplicityAction());
        gui.addAddRoleAction(new AddRoleAction());
        gui.addLoadListener(new LoadListener());
        gui.addSaveListener(new SaveListener());
        gui.addAutomaticHighlightListener(new AutomaticHighlightListener());
        gui.addEditListener(new EditListener());
        gui.addRemoveListener(new RemoveListener());
        gui.addGenerateListener(new GenerateListener());
    }
	
	private class PopupMenuListener extends MouseAdapter {
		public void mouseReleased(MouseEvent e) {
			if (e.isPopupTrigger()) {
				selectedText = gui.getSelectedText();
				gui.showPopupMenu(e);
			}
		}
	}
	
	private class AddClassAction extends AbstractAction {
		static final long serialVersionUID = 1L;

		public AddClassAction() {
			super("Class");
		}
		public void actionPerformed(ActionEvent e) {
			String attributes[] = getAttributesOfClass(selectedText);
			gui.editClass(selectedText, attributes, new ClassOkAction());
        }
	}

	private class ClassOkAction extends AbstractAction {
		private static final long serialVersionUID = 1L;

		public void actionPerformed(ActionEvent e) {
			String className = gui.getClassName();
			String attributes[] = gui.getAttributes();
			ClassObj classObj = model.getAppropriateClass(selectedText);
			if (classObj != null) {
				classObj.setName(className);
			}
			else {				
				dmController.createClass(className);			
			}
			List<Attribute> attrList = dmController.getAttributesOfClass(className);
			// remove all of the attributes
			while (attrList.size() > 0) {
				model.removeAttribute(attrList.get(0));
			}
			// add them back in
			for (int i = 0; i < attributes.length; i++) {
				dmController.createAttribute(className, attributes[i]);
			}
			updateList();
			gui.highlightClass(className);
		}
	}

	private class AddAttributeAction extends AbstractAction {
		static final long serialVersionUID = 1L;

		public AddAttributeAction() {
			super("Attribute");
		}
		public void actionPerformed(ActionEvent e) {
			String classes[] = getClasses();
			gui.editAttribute(selectedText, classes, new AttributeOkAction());
        }
	}

	private class AttributeOkAction extends AbstractAction {
		private static final long serialVersionUID = 1L;

		public void actionPerformed(ActionEvent e) {
			String attributeName = gui.getAttributeName();
			String attributeClass = gui.getAttributeClass();
			dmController.createAttribute(attributeClass, attributeName);
			updateList();
			gui.highlightAttribute(attributeName);
		}
	}

	private class AddAggregationAction extends AbstractAction {
		static final long serialVersionUID = 1L;

		public AddAggregationAction() {
			super("Aggregation");
		}
		public void actionPerformed(ActionEvent e) {
			aggregation = null;
			String classes[] = getClasses();
			gui.editAggregation(selectedText, classes, "", "", "", new AggregationOkAction());
        }
	}

	private class AggregationOkAction extends AbstractAction {
		private static final long serialVersionUID = 1L;

		public void actionPerformed(ActionEvent e) {
			String aggregationName = gui.getAggregationName();
			String wholeClass = gui.getAggregationWholeClass();
			String partClass = gui.getAggregationPartClass();
			String multiplicity = gui.getAggregationMultiplicity();
			if (aggregation == null) {
				dmController.createAggregation(aggregationName, wholeClass, partClass, multiplicity);
			}
			else {
				aggregation.setName(aggregationName);
				aggregation.setSrcClass(model.getAppropriateClass(wholeClass));
				aggregation.setDstClass(model.getAppropriateClass(partClass));
				aggregation.setMultiplicity(multiplicity);
			}
			updateList();
			gui.highlightAggregation(aggregationName);
		}
	}

	private class AddAssociationAction extends AbstractAction {
		static final long serialVersionUID = 1L;

		public AddAssociationAction() {
			super("Association");
		}
		public void actionPerformed(ActionEvent e) {
			association = null;
			String classes[] = getClasses();
			gui.editAssociation(selectedText, classes, "", "", "", "", "", "", new AssociationOkAction());
        }
	}

	private class AssociationOkAction extends AbstractAction {
		private static final long serialVersionUID = 1L;

		public void actionPerformed(ActionEvent e) {
			String associationName = gui.getAssociationName();
			String srcClass = gui.getAssociationSrcClass();
			String destClass = gui.getAssociationDestClass();
			String srcRole = gui.getAssociationSrcRole();
			String destRole = gui.getAssociationDestRole();
			String srcMultiplicity = gui.getAssociationSrcMultiplicity();
			String destMultiplicity = gui.getAssociationDestMultiplicity();
			if (association == null) {
				dmController.createAssociation(associationName, srcClass, destClass,
					srcRole, destRole, srcMultiplicity,	destMultiplicity);
			}
			else {
				association.setName(associationName);
				association.setSrcClass(model.getAppropriateClass(srcClass));
				association.setDestClass(model.getAppropriateClass(destClass));
				association.setSrcRole(srcRole);
				association.setDestRole(destRole);
				association.setSrcMultiplicity(srcMultiplicity);
				association.setDestMultiplicity(destMultiplicity);
			}
			updateList();
			gui.highlightAssociation(associationName);
		}
	}

	private class AddInheritanceAction extends AbstractAction {
		static final long serialVersionUID = 1L;

		public AddInheritanceAction() {
			super("Inheritance");
		}
		public void actionPerformed(ActionEvent e) {
			inheritance = null;
			String classes[] = getClasses();
			gui.editInheritance(selectedText, classes, "", "", new InheritanceOkAction());
        }
	}

	private class InheritanceOkAction extends AbstractAction {
		private static final long serialVersionUID = 1L;

		public void actionPerformed(ActionEvent e) {
			//String inheritanceName = gui.getInheritanceName();
			String baseClass = gui.getInheritanceBaseClass();
			String derivedClass = gui.getInheritanceDerivedClass();
			if (inheritance == null) {
				dmController.createInheritance(baseClass, derivedClass);
			}
			else {
				inheritance.setSrcClass(model.getAppropriateClass(baseClass));
				inheritance.setDstClass(model.getAppropriateClass(derivedClass));
			}
			updateList();
			gui.highlightInheritance("is a");
		}
	}

	private class AddMultiplicityAction extends AbstractAction {
		static final long serialVersionUID = 1L;

		public AddMultiplicityAction() {
			super("Multiplicity");
		}
		public void actionPerformed(ActionEvent e) {
			String relationships[] = new String[aggregationList.size() + associationList.size()];
			for (int i = 0; i < aggregationList.size(); i++) {
				relationships[i] = aggregationList.get(i).getName();
			}
			for (int i = aggregationList.size(); i < (aggregationList.size() + associationList.size()); i++) {
				relationships[i] = associationList.get(i - aggregationList.size()).getName();
			}
			gui.editMultiplicity(selectedText, relationships, new MultRelationshipSelectionAction(), new MultiplicityOkAction());
		}
	}

	private class MultRelationshipSelectionAction extends AbstractAction {
		static final long serialVersionUID = 1L;

		public void actionPerformed(ActionEvent e) {
			String classes[] = new String[2];
			
			int relationshipIndex = gui.getMultiplicityRelationshipIndex();
			if (relationshipIndex >= aggregationList.size()) {
				aggregation = null;
				association = associationList.get(relationshipIndex - aggregationList.size());
				classes[0] = association.getSrcClass().getName();
				classes[1] = association.getDestClass().getName();
			}
			else {
				association = null;
				aggregation = aggregationList.get(relationshipIndex);
				classes[0] = aggregation.getSrcClass().getName();
				classes[1] = aggregation.getDstClass().getName();
			}
			gui.setMultiplicityClasses(classes);
		}
	}
	
	private class MultiplicityOkAction extends AbstractAction {
		private static final long serialVersionUID = 1L;

		public void actionPerformed(ActionEvent e) {
			String multiplicityName = gui.getMultiplicityName();
			String multiplicityClass = gui.getMultiplicityClass();
			if (aggregation == null) {
				String multSrcClass = association.getSrcClass().getName();
				String multDestClass = association.getDestClass().getName();
				dmController.createAssociationMultiplicity(multiplicityName, multSrcClass,
						multDestClass, association.getName(), multSrcClass.matches(multiplicityClass));
			}
			else {
				String multSrcClass = aggregation.getSrcClass().getName();
				String multDestClass = aggregation.getDstClass().getName();
				dmController.createAggregationMultiplicity(multiplicityName, multSrcClass,
						multDestClass, aggregation.getName());
			}
			updateList();
			gui.highlightMultiplicity(multiplicityName);
		}
	}

	private class AddRoleAction extends AbstractAction {
		static final long serialVersionUID = 1L;

		public AddRoleAction() {
			super("Role");
		}
		public void actionPerformed(ActionEvent e) {
			String associations[] = new String[associationList.size()];
			for (int i = 0; i < associations.length; i++) {
				associations[i] = associationList.get(i).getName();
			}
			gui.editRole(selectedText, associations, new RoleAssociationSelectionAction(), new RoleOkAction());
		}
	}

	private class RoleAssociationSelectionAction extends AbstractAction {
		static final long serialVersionUID = 1L;

		public void actionPerformed(ActionEvent e) {
			String classes[] = new String[2];
			
			int associationIndex = gui.getRoleAssociationIndex();
			association = associationList.get(associationIndex);
			classes[0] = association.getSrcClass().getName();
			classes[1] = association.getDestClass().getName();
			gui.setRoleClasses(classes);
		}
	}
	
	private class RoleOkAction extends AbstractAction {
		private static final long serialVersionUID = 1L;

		public void actionPerformed(ActionEvent e) {
			String roleName = gui.getRoleName();
			String roleClass = gui.getRoleClass();
			String roleSrcClass = association.getSrcClass().getName();
			String roleDestClass = association.getDestClass().getName();
			dmController.createRole(roleName, association.getName(), roleSrcClass, 
					roleDestClass, roleSrcClass.matches(roleClass));
			updateList();
			gui.highlightRole(roleName);
		}
	}

	private class LoadListener implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			dmController.loadFromDB();
		}
	}
			
	private class SaveListener implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			dmController.savetoDB();
		}
	}
        
        private class AutomaticHighlightListener implements ActionListener {
		public void actionPerformed(ActionEvent e) 
                {
			//gui.ShowEvent();
                        
                        //--Create the File for the parser to parse
                        String textForFile = gui.getEditorTextPaneObject().getText();
                        PrintWriter pw;
                        try 
                        {
                         pw = new PrintWriter("DEMO_FILE.txt");
                         
                         pw.print(textForFile);
                         pw.close();
                        } 
                        catch (FileNotFoundException ex) 
                        {
                         Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
                        }
                        
                        
                        //--Parse and add to composite phrase
                        DMG.main(new String[] {"DEMO_FILE.txt"});
                        DMParser.main(new String[] {"annotated.txt"});
                        
                        //--Automatically Highlight
                        highlightDomainPhrases();
     
                        
		}//end actionPerformed
	}
			
	private class EditListener implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			int index = gui.getModelConceptIndex();
			if (index < classList.size()) {
				classObj = classList.get(index);
				String attributes[] = getAttributesOfClass(classObj.getName());
				gui.editClass(classObj.getName(), attributes, new ClassOkAction());
			}
			else if (index < classList.size() + associationList.size()) {
				association = associationList.get(index - classList.size());
				String classes[] = getClasses();
				gui.editAssociation(association.getName(), classes, association.getSrcClass().getName(),
						association.getDestClass().getName(), association.getSrcRole(),
						association.getDestRole(), association.getSrcMultiplicity(),
						association.getDestMultiplicity(), new AssociationOkAction());
			}
			else if (index < classList.size() + associationList.size() + aggregationList.size()) {
				aggregation = aggregationList.get(index - classList.size() - associationList.size());
				String classes[] = getClasses();
				gui.editAggregation(aggregation.getName(), classes, aggregation.getSrcClass().getName(), 
						aggregation.getDstClass().getName(), aggregation.getMultiplicity(), new AggregationOkAction());
			}
			else if (index < classList.size() + associationList.size() + aggregationList.size() + inheritanceList.size()) {
				inheritance = inheritanceList.get(index - classList.size() - associationList.size() - aggregationList.size());
				String classes[] = getClasses();
				gui.editInheritance(inheritance.getName(), classes, inheritance.getSrcClass().getName(), 
						inheritance.getDstClass().getName(), new InheritanceOkAction());
			}
			else {
				System.out.println("Error in EditListener");
			}
		}		
	}

	private class RemoveListener implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			List<String> unhighlightList = new ArrayList<String>();
			
			int index = gui.getModelConceptIndex();

			if (index < classList.size()) {
				ClassObj classObj = classList.get(index);
				ArrayList<Attribute> attrArray = classList.get(index).getAttributes();
				for (Attribute attr : attrArray) {
					unhighlightList.add(attr.getName());
				}
				unhighlightList.add(classObj.getName());
				removeController.removeClass(classObj);
			}
			else if (index < classList.size() + associationList.size()) {
				Association association = associationList.get(index - classList.size());
				unhighlightList.add(association.getDestMultiplicity());
				unhighlightList.add(association.getDestRole());
				unhighlightList.add(association.getSrcMultiplicity());
				unhighlightList.add(association.getSrcRole());
				unhighlightList.add(association.getName());
				removeController.removeAssociation(association);			
			}
			else if (index < classList.size() + associationList.size() + aggregationList.size()) {
				Aggregation aggregation = aggregationList.get(index - classList.size() - associationList.size());
				unhighlightList.add(aggregation.getMultiplicity());
				unhighlightList.add(aggregation.getName());
				removeController.removeAggregation(aggregation);						
			}
			else if (index < classList.size() + associationList.size() + aggregationList.size() + inheritanceList.size()) {
				Inheritance inheritance = inheritanceList.get(index - classList.size() - associationList.size() - aggregationList.size());
				//TODO currently no name for inheritance, which is a problem
				//unhighlightList.add(inh.getName());
				// for now assume name is always "is a"
				unhighlightList.add("is a");
				removeController.removeInheritance(inheritance);						
			}
			else {
				System.out.println("Error in RemoveListener");
			}
			
			for (int i = 0; i < unhighlightList.size(); i++) {
				gui.unhighlight(unhighlightList.get(i));
			}
			updateList();
		}		
	}

	private class GenerateListener implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			genDomainModelController.generateModel();
			gui.displayDomainModel();
		}
	}
	
	private String[] getClasses() {
		List<ClassObj> classList = dmController.getClassList();
		String classes[] = new String[classList.size()];
		for (int i = 0; i < classes.length; i++) {
			classes[i] = classList.get(i).getName();
		}
		return classes;
	}

	private String[] getAttributesOfClass(String className) {
		List<Attribute> attrList = dmController.getAttributesOfClass(className);
		String attributes[] = new String[attrList.size()];
		for (int i = 0; i < attributes.length; i++) {
			attributes[i] = attrList.get(i).getName();
		}
		return attributes;
	}
	
	private String getClassDescription(ClassObj c) {
		int i;
		
		String description = "(Class)" + c.getName() + "(";
		ArrayList<Attribute> attributeList = c.getAttributes();
		for (i = 0; i < (attributeList.size() - 1); i++) {
			description += attributeList.get(i).getName() + ", ";
		}

		if (attributeList.size() > 0) {
			description += attributeList.get(i).getName();			
		}
		
		description += ")";
		
		return description;
	}
	
	private String getAssociationDescription(Association a) {
		String description = "(Association)" + a.getName() + "(";
		description += a.getSrcClass().getName() + "(" + a.getSrcRole() + ")(" + a.getSrcMultiplicity() + "), "; 
		description += a.getDestClass().getName() + "(" + a.getDestRole() + ")(" + a.getDestMultiplicity() + "))";

		return description;
	}
	
	private String getAggregationDescription(Aggregation a) {
		String description = "(Aggregation)" + a.getName() + "(";
		description += a.getSrcClass().getName() + ", ";
		description += a.getDstClass().getName() + "(" + a.getMultiplicity() + "))";
		
		return description;
	}
	
	private String getInheritanceDescription(Inheritance i) {
		String description = "(Inheritance)(";
		description += i.getSrcClass().getName() + ", ";
		description += i.getDstClass().getName() + ")";
		
		return description;
	}
	
	private void updateList() {
		classList = dmController.getClassList();
		associationList = dmController.getAssociationList();
		aggregationList = dmController.getAggregationList();
		inheritanceList = dmController.getInheritanceList();
		List<String> mcList = new ArrayList<String>();
				
		for (int i = 0; i < classList.size(); i++) {
			mcList.add(getClassDescription(classList.get(i)));
		}
		
		for (int i = 0; i < associationList.size(); i++) {
			mcList.add(getAssociationDescription(associationList.get(i)));
		}
		
		for (int i = 0; i < aggregationList.size(); i++) {
			mcList.add(getAggregationDescription(aggregationList.get(i)));
		}

		for (int i = 0; i < inheritanceList.size(); i++) {
			mcList.add(getInheritanceDescription(inheritanceList.get(i)));
		}
		
		String mcArray[] = new String[mcList.size()];
		mcList.toArray(mcArray);
                gui.setModelConceptList(mcArray);
	}

	private void initTestData() {
//		dmController.createClass("car");
//		dmController.createAttribute("car", "color");
//		dmController.createAttribute("car", "doors");
//				
//		dmController.createClass("engine");
//		dmController.createAttribute("engine", "horsepower");
//		dmController.createAttribute("engine", "size");
//
//		dmController.createAssociation("powers", "engine", "car", "", "", "", "");
//
//		dmController.createClass("jack");
//		dmController.createAssociation("lifts", "jack", "car", "", "", "", "");
//
//		dmController.createClass("seat");
//		dmController.createAttribute("seat", "material");
//
//		dmController.createAggregation("contains", "car", "seat", "");
//
//		dmController.createClass("vehicle");
//
//		dmController.createInheritance("vehicle", "car");
//		updateList();
	}

        public void highlightDomainPhrases()
        {
            CompositePhrase phrases = CompositePhrase.getInstance();

            try
            {
                for(int i = 0; i < phrases.getPhrases().size(); i++)
                {
                    String searchFor = phrases.getPhrases().get(i).getValue();
                    Color highLightColor = phrases.getPhrases().get(i).getColor();
                    
                    String temp = phrases.getPhrases().get(i).getType();
                    String associationType = phrases.getPhrases().get(i).getAssociations();
                   
                    populateDomainObjects(searchFor, temp, associationType);
                    updateList();
                    
                    gui.highlightAllInstances(searchFor, highLightColor);
                    //System.out.println("Word: " + searchFor + ".....Type: " + associationType + ".....Color : " + highLightColor.toString());
                }
            }
            catch(Exception x)
            {
                // Null case, ignore it and move on
            }
        }
        
        public void populateDomainObjects(String word, String type, String associationType)
        {
            if(type.equalsIgnoreCase("class"))
            {
                dmController.createClass(word);
            }
            if(type.equalsIgnoreCase("attribute"))
            {
                dmController.createAttribute(associationType, word);
            }
            if(type.equalsIgnoreCase("aggregation"))
            {
                String[] assocations = associationType.split("#");
                //--0 = parent
                //--1 = child
                
                dmController.createAggregation(word,
                                               assocations[0],
                                               assocations[1],
                                               "*"
                                              );

            }
            if(type.equalsIgnoreCase("association"))
            {
                //--method arguments...
                //--String name, String srcClass,
		//--String destClass, String srcRole, String destRole,
		//--String srcMultiplicity, String destMultiplicity
                String[] assocations = associationType.split("#");
                dmController.createAssociation(word,            //name
                                               assocations[0],       //srcClass
                                               assocations[1],       //destClass
                                               "srcRole",            //srcRole
                                               "destRole",           //destRole
                                               "*",                  //srcMultipli
                                               "*"                   //destMulti
                                              );
            }
        }
}
